// Icon widget implmenentation for Gtk+
// Copyright 2006 (C) Ralph Thomas

#include <view/icon.h>
#include <view/gtk/common.h>
#include <toolkit/gtk/icon-gtk.h>
#include <binder.hpp>
#include <gtk/os_utilities.hpp>

namespace view {
	//
	/// For some reason, the icons come back larger than we want so we
	/// ask for a smaller size.
	///
	/// \param	size	the size we want.
	/// \return	the size to ask for.
	//
	long shrink_size( long size ) {
		if( size == 22 ) return 16;
		if( size == 32 ) return 22;
		if( size == 48 ) return 32;
		if( size == 64 ) return 48;
		return size;
	}
	void update_icon( GtkWidget* widget, long pixel_size, const adobe::value_t& value ) {
		GtkImage* image = GTK_IMAGE(widget);
		if( !image ) return;
		//
		// If the value is a string then assume that it's the name of a stock icon.
		//
		if( value.type() == typeid(std::string) ) {
			std::string stock_name( value.get<std::string>() );
			gtk_image_set_from_stock( image, stock_name.c_str(), GTK_ICON_SIZE_MENU );
			return;
		}
		//
		// Try to extract the icon.
		//
		try {
			toolkit::icon_gtk_ptr_t icon_ptr;
			value.get( icon_ptr );
			//
			// If we have the icon then setup the
			// image with it.
			//
			if( icon_ptr ) {
				GdkPixbuf* pbuf( icon_ptr->get_pixbuf( shrink_size( pixel_size ) ) );
				if( !pbuf ) return;
				long x( 0 ); long y( 0 );
				long w( gdk_pixbuf_get_width( pbuf ) );
			       	long h( gdk_pixbuf_get_height( pbuf ) );
				//
				// Only scale if we have to.
				//
				if( w > pixel_size || h > pixel_size ) {
					scale_aspect( x, y, w, h, pixel_size, pixel_size );
					GdkPixbuf* new_buf = gdk_pixbuf_scale_simple( pbuf, w, h, GDK_INTERP_BILINEAR );
					g_object_unref( pbuf );
					pbuf = new_buf;
				}
				gtk_image_set_from_pixbuf( image, pbuf );
				//
				// XXX: Not sure that I understand this unref any more.
				//
				g_object_unref( pbuf );
			} else gtk_image_clear( image );
		} catch( ... ) { gtk_image_clear( image ); }
	}
	adobe::widget_node_t icon_factory(
		const adobe::dictionary_t& 	parameters,
		const adobe::widget_node_t&	parent,
		const adobe::factory_token_t&	token )
	{
		//
		// It's invalid to not have a "bind" parameter.
		//
		adobe::value_t bind = parameters.at( adobe::static_name_t( "bind" ) );
		//
		// The Gtk icon widget is really simple. It's a GtkImage
		// that is assigned a fixed size in Eve.
		//
		GtkWidget* image = gtk_image_new();
		token.assemblage_m.delete_on_destruction( image );
		//
		// Get the pixel-size out. This is set into the image and
		// is converted to a GtkIconSize that's given to update_image.
		//
		int size( 48 );
		parameters.get<int>( adobe::static_name_t( "size" ), size );
		g_object_set( G_OBJECT(image), "pixel-size", size, NULL );
		//
		// Create a binder to look after keeping the icon up-to-date.
		//
		adobe::binder_helper_t* binder = new adobe::binder_helper_t( token.sheet_m );
		binder->initialize( bind, boost::bind( &update_icon, image, size, _1 ), 0 );
		token.assemblage_m.delete_on_destruction( binder );
		//
		// Insert this widget into it's parent.
		//
		adobe::display_t::position_t display_token( adobe::insert( adobe::get_main_display(), parent.display_token_m, image ) );
		adobe::display_t::position_t overlay_token( token.overlay_m.insert( parent.overlay_token_m ) );
		//
		// Bind the calculate and place signals for Eve.
		//
		adobe::eve_t::signal_suite_t signals;
		adobe::extents_t extents;
		eve_client::place_func_t placer = boost::bind( &adobe::implementation::set_bounds_widget, image, _1, _2 );
		extents.width() = extents.height() = size;
		signals.eve_calculate_proc_m = boost::bind( &eve_client::utility_widget_calculate, extents, _1 );
		signals.eve_place_proc_m = boost::bind( &eve_client::utility_widget_place, placer, _1, _2, boost::ref( token.overlay_m ), overlay_token );
		//
		// Register the widget with Eve.
		//
		adobe::eve_t::iterator eve_token(
			token.layout_m.add_view_element( parent.eve_token_m,
				adobe::eve_t::insert_element_t(
					adobe::eve_t::calculate_data_t(),
					false,
					parameters,
					signals )
				)
			);
		//
		// Return the new icon view.
		//
		return adobe::widget_node_t( parent.size_m, eve_token, display_token, overlay_token );
	}
}
